package com.lost.octopus.es.processor.config.es;

import org.apache.commons.lang3.StringUtils;;
import com.lost.octopus.es.processor.config.es.security.MyX509TrustManager;
import com.lost.octopus.es.processor.config.es.security.NullHostNameVerifier;
import com.lost.octopus.es.processor.config.es.security.SecuredHttpClientConfigCallback;
import com.lost.octopus.es.processor.config.properties.ElasticsearchModeProperties;
import lombok.extern.log4j.Log4j2;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.List;

/**
 * es 连接模式工厂
 * @author zhangbh
 **/
@Log4j2
@Configuration
public class ElasticsearchModeInitFactory extends AbstractElasticsearchConfiguration {


    @Resource
    private ElasticsearchRestClientProperties elasticsearchRestClientProperties;
    /**
     * 模块配置
     */
    @Resource
    private ElasticsearchModeProperties elasticsearchModeProperties;
    @Resource
    private RestClientBuilder restClientBuilder;


    /**
     * 初始化
     * @param clusterAddress 访问地址 http://192.168.0.82:9200
     * @param hasCer 是否验证认证文件
     * @return 客户端
     */
    public RestHighLevelClient huaWeiCloudLoad(String clusterAddress, boolean hasCer) {
        String userName = elasticsearchRestClientProperties.getUsername();
        String password = elasticsearchRestClientProperties.getPassword();

        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
        SSLContext sc;
        try {
            sc = hasCer ? hasCerFileLoad() : noCerFileLoad();
        } catch (Exception e) {
            log.error("es安全连接异常", e);
            return null;
        }
        SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sc, new NullHostNameVerifier());
        SecuredHttpClientConfigCallback httpClientConfigCallback = new SecuredHttpClientConfigCallback(sessionStrategy, credentialsProvider);
        // http://192.168.0.82:9200
        URI uri;
        try {
            uri = new URI(clusterAddress);
        } catch (URISyntaxException e) {
            log.error("spring.elasticsearch.rest.uris 语法不符合URI标准");
            throw new RuntimeException(String.format("spring.elasticsearch.rest.uris 地址 %s 语法不符合URI标准", clusterAddress));
        }
        RestClientBuilder builder = RestClient.builder(new HttpHost(uri.getHost(), uri.getPort(), "https"))
            .setHttpClientConfigCallback(httpClientConfigCallback);
        return new RestHighLevelClient(builder);
    }

    /**
     * 跳过证书验证
     * @return 认证管理
     */
    private SSLContext noCerFileLoad() throws NoSuchAlgorithmException, KeyManagementException {
        TrustManager[] noAuth = {
            new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            }};
        SSLContext ssl = SSLContext.getInstance("SSL");
        // 自定义初始化
        ssl.init(null, noAuth, new SecureRandom());
        return ssl;
    }

    private SSLContext hasCerFileLoad() throws Exception {
        String filePath = elasticsearchModeProperties.getCertFilePath();
        String password = elasticsearchModeProperties.getCerPassword();
        if (StringUtils.isBlank(filePath) || StringUtils.isBlank(password)) {
            throw new RuntimeException("elasticsearch 在安全证书模式下，缺少配置  custom.elasticsearch.cerPassword "
                + "或 custom.elasticsearch.certFilePath ");
        }
        TrustManager[] tm = {new MyX509TrustManager(filePath, password)};
        SSLContext ssl = SSLContext.getInstance("SSL", "SunJSSE");
        ssl.init(null, tm, new SecureRandom());
        return ssl;
    }


    public RestHighLevelClient get() {
        List<String> uris = elasticsearchRestClientProperties.getUris();
        if (CollectionUtils.isEmpty(uris)) {
            throw new RuntimeException("获取配置失败，请确认配置：spring.elasticsearch.rest.uris!");
        }
        String clusterAddress = uris.get(0);
        // 默认获取一个 http://192.168.0.82:9200
        log.info("elasticsearch 连接，使用模式={}", elasticsearchModeProperties.getMode().getLabel());
        switch (elasticsearchModeProperties.getMode()) {
            case HUAWEI_SSL_NO_CER:
                return huaWeiCloudLoad(clusterAddress, false);
            case HUAWEI_SSL_HAS_CER:
                return huaWeiCloudLoad(clusterAddress, true);
            default:
                return new RestHighLevelClient(restClientBuilder);
        }
    }

    @Bean
    @Override
    public RestHighLevelClient elasticsearchClient() {
        return get();
    }
}
